package qj.tool.jna;


import static qj.tool.jna.User32.MouseMessages.*;

import java.awt.Point;

import qj.tool.jna.User32.*;
import qj.util.RegexUtil;
import qj.util.funct.F1;
import qj.util.funct.F2;
import qj.util.funct.F3;
import qj.util.funct.P0;

import com.sun.jna.examples.win32.W32API;
import com.sun.jna.examples.win32.W32API.HMODULE;
import com.sun.jna.examples.win32.W32API.LRESULT;
import com.sun.jna.examples.win32.W32API.WPARAM;

public class JnaUtil {
	
	
	public static void main(String[] args) {
//		User32.INSTANCE.LockWorkStation();
		System.out.println(getCursorType());
	}
	
	public static String getCursorType() {
		User32 user32 = User32.INSTANCE;
		
		CURSORINFO ci = new CURSORINFO();
		ci.cbSize = new W32API.DWORD((long)ci.size());
		user32.GetCursorInfo(ci);

		if (ci.hCursor == null) {
			return null;
		}
		return RegexUtil.getString("@(\\w+)$", 1, ci.hCursor.toString());
	}
	
	public static boolean terminated = false;
	
	public static void setKeyboardHook(final F2<Integer, Boolean, Boolean> funct) {
		new Thread(new Runnable() {public void run() {
	        final User32 lib = User32.INSTANCE;
	        HMODULE hMod = Kernel32.INSTANCE.GetModuleHandle(null);
	        
	        LowLevelKeyboardProc keyboardHook = new LowLevelKeyboardProc() {
	            public LRESULT callback(int nCode, WPARAM wParam, KBDLLHOOKSTRUCT info) {
					
	                if (nCode >= 0) {
	                    Integer key = info.vkCode;
						Boolean pressed = isPressed(wParam);
//						System.out.println(key);
						return funct.e(key, pressed) ? User32.CALL_NEXT_HOOK : User32.SKIP_HOOK;
	                }
					return User32.CALL_NEXT_HOOK;
	            }
	
				private boolean isPressed(WPARAM wParam) {
					switch(wParam.intValue()) {
					case User32.WM_KEYUP:
					case User32.WM_SYSKEYUP:
						return false;
					default:
						return true;
					}
				}
	        };
	        final HHOOK[] hhk = {null};
	        
	        shutdownF = new P0() {public void e() {
	    		terminated = true;
//				System.out.println("Setting terminated = true");
	    		if (hhk[0] != null) {
	    	        User32.INSTANCE.UnhookWindowsHookEx(hhk[0]);
//	    		        log.debug("Unhooked");
	    	        hhk[0] = null;
	    		}	
	    	}};
	    	
	        // Add shutdown hook to unhook keyboard
			Runtime.getRuntime().addShutdownHook(new Thread() {public void run() {
				shutdownF.e();
			}});
			while (!terminated) {
				try {
			        hhk[0] = lib.SetWindowsHookEx(User32.WH_KEYBOARD_LL, keyboardHook, hMod, 0);
					int getMessage;
					while ((getMessage = User32.INSTANCE.GetMessage(new User32.MSG(), null, 0, 0)) == 1 && !terminated) {
						System.out.println("ERROR: getMessage terminated: " + getMessage);
					}
					System.out.println("ERROR: getMessage terminated: " + getMessage);
				} catch (Throwable e) {
					e.printStackTrace();
				}
			}
//			System.out.println("Terminated");

		}}).start();
	}

	public static P0 shutdownF = new P0() {public void e() {
		terminated = true;
	}};
	public static class MouseInfo {
		Point point;
	}
	
	public static void setMouseHook(final F1<Point, Boolean> onMove, final F3<Integer, Boolean, Point, Boolean> onButton) {
		new Thread(new Runnable() {public void run() {
	        final User32 lib = User32.INSTANCE;
	        HMODULE hMod = Kernel32.INSTANCE.GetModuleHandle(null);
	        LowLevelMouseProc mouseHook = new LowLevelMouseProc() {
	            public LRESULT callback(int nCode, WPARAM wParam, MSLLHOOKSTRUCT info) {
//                	System.out.println(wParam.longValue() + ", " + nCode + ", " + info.pt.x + ", " + info.flags + ", " + info.mouseData + ", " + info.dwExtraInfo.longValue());
	                if (nCode >= 0) {
	                	int action = wParam.intValue();
						Point point = new Point(info.pt.x, info.pt.y);
						if (WM_MOUSEMOVE == action) {
							return onMove == null || !onMove.e(point) ? User32.SKIP_HOOK : User32.CALL_NEXT_HOOK;
						}
						
                		if (onButton != null) {
							Integer button = getButton(info, action);
							boolean pressed = getPressed(info, action);
							return onButton.e(button, pressed, point) ? User32.CALL_NEXT_HOOK : User32.SKIP_HOOK;
						}
	                }
					return User32.CALL_NEXT_HOOK;
	            }

				private int getButton(MSLLHOOKSTRUCT info, int action) {
					switch (action) {
					case WM_MBUTTONDOWN:
						return 2;
					case WM_RBUTTONDOWN:
						return 3;
					case WM_LBUTTONDOWN:
						return 1;
					case WM_MBUTTONUP:
						return 2;
					case WM_RBUTTONUP:
						return 3;
					case WM_LBUTTONUP:
						return 1;
					case WM_MOUSEWHEEL:
						return 4;
					case WM_SBUTTONDOWN:
					case WM_SBUTTONUP:
						return info.mouseData == WD_SIDE1 ? 5 : 6;
					default:
						return -1;
					}
				}
				private boolean getPressed(MSLLHOOKSTRUCT info, int action) {
					switch (action) {
					case WM_MBUTTONDOWN:
					case WM_RBUTTONDOWN:
					case WM_LBUTTONDOWN:
						return true;
					case WM_MBUTTONUP:
					case WM_RBUTTONUP:
					case WM_LBUTTONUP:
						return false;
					case WM_MOUSEWHEEL:
						return info.mouseData < 0;
					case WM_SBUTTONDOWN:
						return true;
					case WM_SBUTTONUP:
						return false;
					default:
						return false;
					}
				}

	        };

	        final HHOOK hhk = lib.SetWindowsHookEx(User32.WH_MOUSE_LL, mouseHook, hMod, 0);

	        // Add shutdown hook to unhook keyboard
			Runtime.getRuntime().addShutdownHook(new Thread() {public void run() {
				terminated = true;
				if (hhk != null) {
			        User32.INSTANCE.UnhookWindowsHookEx(hhk);
//			        log.debug("Unhooked");
				}
			}});
			int getMessage;
			while ((getMessage = User32.INSTANCE.GetMessage(new User32.MSG(), null, 0, 0)) == 1 && !terminated) {
				System.out.println("ERROR: getMessage terminated: " + getMessage);
			}
			System.out.println("ERROR: getMessage terminated: " + getMessage);
		}}).start();
	}

}
